package org.jflame.web.spring.jackson;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;

import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.util.StreamUtils;
import org.springframework.util.TypeUtils;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;

/**
 * 重写MappingJackson2HttpMessageConverter,支持动态过滤接口返回属性
 * 
 * @see JacksonDynamicFilterResponseBodyAdvice
 * @see JacksonFilters
 * @see JacksonFilter
 * @author zyc
 */
public class MyMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    public MyMappingJackson2HttpMessageConverter() {
        super();
    }

    public MyMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        if (object instanceof MappingJacksonValue) {
            MappingJacksonValue container = (MappingJacksonValue) object;
            if (container.getFilters() != null && container.getFilters() instanceof JacksonDynamicFilter) {
                Class<?> clazz = container.getValue()
                        .getClass();
                MediaType contentType = outputMessage.getHeaders()
                        .getContentType();
                JsonEncoding encoding = getJsonEncoding(contentType);
                ObjectMapper objectMapper = getObjectMapper().copy();
                OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
                try (JsonGenerator generator = objectMapper.getFactory()
                        .createGenerator(outputStream, encoding)) {
                    writePrefix(generator, object);
                    JacksonDynamicFilter jdfilter = (JacksonDynamicFilter) container.getFilters();
                    jdfilter.addMix(objectMapper);
                    ObjectWriter objectWriter = objectMapper.writer();
                    objectWriter = objectWriter.with(jdfilter);

                    if (type != null && TypeUtils.isAssignable(type, clazz)) {
                        JavaType javaType = getJavaType(type, null);
                        if (javaType != null && javaType.isContainerType()) {
                            objectWriter = objectWriter.forType(javaType);
                        }
                    }

                    objectWriter.writeValue(generator, container.getValue());

                    writeSuffix(generator, object);
                    generator.flush();
                } catch (InvalidDefinitionException ex) {
                    throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
                } catch (JsonProcessingException ex) {
                    throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
                }
                return;
            }
        }
        super.writeInternal(object, type, outputMessage);

    }

}
